1 Get the data

Trying Rcpp with my galleryGM2R package

library(galleryGm2R)
library(dplyr)
library(stringr)
library(readr)
library(ggplot2)
library(plotly)
library(purrr)

Here’s the C++ code to load the GhostDetctorArtRecord and TrackingActionArtRecord data.

#include <Rcpp.h>
#include <vector>
#include <string>

// [[Rcpp::depends(galleryGm2R)]]

#include "canvas/Utilities/InputTag.h"
#include "gallery/Event.h"
#include "gallery/ValidHandle.h"

#include "GhostDetectorArtRecordDF.hh"
#include "TrackingActionArtRecordDF.hh"
#include "GalleryTimer.hh"

using namespace std;

// [[Rcpp::export]]
Rcpp::List getDFs(vector<string> files) {
  if ( files.size() < 1) { Rcpp::stop("You must specify at least one file to process"); }
 
  // Input tags
  art::InputTag const ghost_tag{"artg4:GhostRingDetector"};
  art::InputTag const trackingAction_tag{"artg4:"};
  
  // Timer
  GalleryTimer timer;
 
  // Dataframes
  GhostDetectorArtRecordDF gDF;
  TrackingActionArtRecordDF taDF;
 
  // Loop over events
  for ( gallery::Event ev(files); !ev.atEnd(); ev.next() ) {
    timer.beginTiming();
  
    gDF.fill(ev, ghost_tag);
    taDF.fill(ev, trackingAction_tag);

    timer.endTiming(); 
  }
  
  timer.write();
  
  return Rcpp::List::create(
    Rcpp::Named("gDF")  = gDF.df(),
    Rcpp::Named("taDF") = taDF.df()
  );
}

Renee has a file that I can get to in XRootD. I think this one has everything turned on.

system(
  "ifdh ls /pnfs/GM2/scratch/users/rfatemi/MIgun/gm2ringsim_muon_A.root", intern = T) %>% 
       paste0('root://fndca1.fnal.gov', .) -> aFile

Let’s read it

allDfs <- getDFs(aFile)
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/rfatemi/MIgun/gm2ringsim_muon_A.root
Closing file, read 16427428 bytes in 59 transactions
Processed 1000 events in an average of 2500 microseconds/event
Total processing time (including file opening) was 180598 milliseconds
tadf <- tbl_df(allDfs$taDF)
ghdf <- tbl_df(allDfs$gDF)

How much memory

library(pryr)
object_size(tadf)
54.5 MB
object_size(ghdf)
8.22 MB

2 Get VolumeID information

We need to convert the VolumeID to a Volume Name for the tracking action art record. The “physical volume store” table is buried in the art file Run record, which is inaccessible to gallery. I wrote a small analyzer to simply dump the physical volume store (pvs) to a file. Remember that the pvs can change per file.

runThis <- str_interp(
  "PVS_CSVOUT=muonA.csv gm2 -c ${fclPath}/fcl/physicalVolumeStoreToFile.fcl -n 1 ${aFile}",
  list(fclPath=Sys.getenv("GM2ANALYSES_DIR"), aFile=aFile))
system(runThis)
Art has completed and will exit with status 90.

Load this in…

muonA_pvs <- read_csv("muonA.csv", col_names = c("volID", "volName"))
Parsed with column specification:
cols(
  volID = col_integer(),
  volName = col_character()
)

What did we get?

muonA_pvs

We can use this data for making factors.

tadf %>% mutate( volName = factor(volumeUID, levels=muonA_pvs$volID, labels=muonA_pvs$volName ) ) -> tadf

3 Look at primary muon death

Let’s just look at the primary muons (maybe later do this selection in the gallery code) and we don’t want the birth. We only need the tracking action data for this.

tadf %>% filter(parentTrackID == 0, status != 0) -> pmdf
library(ggplot2)

3.1 Death by volume from tracking action

Plot the volume for the dying muon.

pmdf %>% ggplot(aes(volName)) + geom_bar()

pmdf %>% count(volName) %>% mutate(percent=n/sum(n)*100)

3.2 Death by position

The ones in the arc are the decays to positrons (and they nicely show where the ring is). The ones that hit the world are lost.

pmdf %>% plot_ly(x=~map_dbl(pos, 1), y=~map_dbl(pos, 3), z=~map_dbl(pos,2),
                 color=~as.character(volName),
                 colors = c('#BF382A', '#0C4B8E') ) %>% 
  add_markers(size=I(2)) %>% 
  layout(scene = list(xaxis = list(title = 'x'),
                     yaxis = list(title = 'z'),
                     zaxis = list(title = 'y'))) -> p
p

You can spin the plot around and zoom in with your mouse!

3.3 Position and momentum

Let’s see if we can add lines showing the momentum direction…

Let’s convert the momentum vectors into unit vectors (note that p is indeed the magnitude of the mom vector)…

pmdf %>% mutate( ipx = map_dbl(mom,1)/p, ipy = map_dbl(mom,2)/p, ipz = map_dbl(mom,3)/p ) -> pmdf

Apparenlty, Plotly can’t draw 3D segments (bummer!). Switch to rgl.

library(rgl)

Need a vector for coloring by volume name…

volColor <- with(pmdf,
                 ifelse(volName=='world', 'black', 'red')
)
plot3d(x=map_dbl(pmdf$pos, 1), y=map_dbl(pmdf$pos, 3), z=map_dbl(pmdf$pos, 2),
       type="p",
       xlab="x (mm)", ylab="z (mm)", zlab="y (mm)", col=volColor, 
       main = 'mu+ death position',
       sub = 'red=decay in ring, black=exit at world boundary'
       )
rglwidget()

Let’s plot the momentum unit vectors

interleave <- function(v1, v2) as.vector(rbind(v1, v2))
plot3d(x=map_dbl(pmdf$pos, 1), y=map_dbl(pmdf$pos, 3), z=map_dbl(pmdf$pos, 2),
       type="p",
       xlab="x", ylab="z", zlab="y", col=volColor, 
       main="mu+ death position and momentum unit vector",
       sub = 'red=decay in ring, black=exit at world boundary')
       
# For segments, we have the following
s <- 500
m <- data.frame(
  xs = map_dbl(pmdf$pos,1),
  ys = map_dbl(pmdf$pos,2),
  zs = map_dbl(pmdf$pos,3),
  xe = map_dbl(pmdf$pos,1) + pmdf$ipx*s,
  ye = map_dbl(pmdf$pos,2) + pmdf$ipy*s,
  ze = map_dbl(pmdf$pos,3) + pmdf$ipz*s,
  main = 'mu+ death position',
  sub = 'red=decay in ring, black=exit at world boundary'
)
# Interleave and display
# See http://stackoverflow.com/questions/26853717/using-rgl-to-plot-3d-line-segments-in-r
with(m,
  segments3d(x=interleave(xs, xe),
             y=interleave(zs, ze),
             z=interleave(ys, ye),
             col=rep(volColor, each=2), alpha=0.5
  )
)
rglwidget()

Again, you can spin around the image and zoom in and out. I can’t draw arrows (bummer). The momentum direction is indicated by the direction of the little line segment (it starts at the point were the muon is lost; the dot - all of the black dots are at the world volume edge). The length is merely the same for all segments (only shows the momentum direction, not its magnitude).

Instead of using a unit vector for the momentum, let’s try the real vector. They’ll likely need to be scaled down. It would also be helpful to draw where the inflector and kicker mangets are.

clear3d()
s <- 0.5
with(pmdf, {
    plot3d(x=map_dbl(pos, 1), y=map_dbl(pos, 3), z=map_dbl(pos, 2),
          type="p",
          xlab="x", ylab="z", zlab="y", col=volColor,
          main="mu+ death position and momentum vector",
          sub = 'red=decay in ring, black=exit at world boundary'
    )
  
    segments3d(x = interleave(map_dbl(pos, 1), map_dbl(pos, 1) + map_dbl(mom, 1)*s),
               y = interleave(map_dbl(pos, 3), map_dbl(pos, 3) + map_dbl(mom, 3)*s),
               z = interleave(map_dbl(pos, 2), map_dbl(pos, 2) + map_dbl(mom, 2)*s),
              col=rep(volColor, each=2)
    )
    
    cyl <- cylinder3d( center=rbind(c(0,0,-50), c(0,0,50)),
            radius=7112,
            e1=cbind(0,0,1),
            e2=cbind(1,0,0), sides=20, closed = F)
    plot3d(cyl, add = T, alpha=0.1)
    
  }
)
rglwidget()

3.4 Connect hits in the Ghost ring detector to the world exit

Would be nice to connect the ghost hits to the world hits to see if things are crossing the ring. Let’s try that.

head(ghdf)

Just pick the muons

ghdf %>% filter(trackID == 1) -> ghmdf
ghmdf

Connect these to the tracking action data (we need to choose only the ones that escape the world, not the ones that decay). The world (for this file) is volID == 14.

pmdf %>% filter(volumeUID == 14) -> pmwdf
paste("nrow(pmwdf)=", nrow(pmwdf), " nrow(ghmdf)=", nrow(ghmdf))
[1] "nrow(pmwdf)= 954  nrow(ghmdf)= 19409"

Uh - why are there so many ghost hits? Looks like at a particular event.

pmwdf %>% filter(eventEntry == 4) -> pmw4df
pmw4df
ghmdf %>% filter(eventEntry == 4)

Whaa?? Try to plot this. We’ll plot the ghost points and connect them with lines. And then plot a line from the last ghost point to the world exit.

Get the last ghost point for this event

ghmdf %>% filter( eventEntry == 4) %>% slice(n()) -> ghm4df

Make a data frame for this last point

gh2wd <- data.frame( x=c(map_dbl(ghm4df$pos, 1), map_dbl(pmw4df$pos, 1)),
                     y=c(map_dbl(ghm4df$pos, 2), map_dbl(pmw4df$pos, 2)),
                     z=c(map_dbl(ghm4df$pos, 3), map_dbl(pmw4df$pos, 3)))
gh2wd
rgl.clear()
with( ghmdf %>% filter(eventEntry == 4),{
    plot3d( x = map_dbl(pos, 1),
            y = map_dbl(pos, 3),
            z = map_dbl(pos, 2), type="p", col="green",
            xlim=c(-10000, 10000), ylim=c(-10000, 10000), zlim=c(-600, 600),
            xlab='x', ylab='z', zlab='y'
    )
  
    lines3d(x = map_dbl(pos, 1),
            y = map_dbl(pos, 3),
            z = map_dbl(pos, 2) )
  
    cyl <- cylinder3d( center=rbind(c(0,0,-50), c(0,0,50)),
                radius=7112,
                e1=cbind(0,0,1),
                e2=cbind(1,0,0), sides=20, closed = F)
    plot3d(cyl, add = T, alpha=0.1)
})
with( pmwdf %>% filter(eventEntry == 4), {
  points3d( x = map_dbl(pos, 1),
            y = map_dbl(pos, 3),
            z = map_dbl(pos, 2), col="red")
})
with( gh2wd, 
      lines3d(x=x, y=z, z=y, col="red"))
rglwidget()

This really makes no sense. I’m puzzled as to why there are so many ghost hits with hard turns. We should look at the steps in ParaView. OR - we can look at the Geant steps

3.5 What really would be better is a cylindrical world volume

Working on this

Aaron likes \(x^2\).

LS0tCnRpdGxlOiAiQVJSIExvc3QgTXVvbnMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHllcwotLS0KCgojIEdldCB0aGUgZGF0YQoKVHJ5aW5nIFJjcHAgd2l0aCBteSBgZ2FsbGVyeUdNMlJgIHBhY2thZ2UKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGdhbGxlcnlHbTJSKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocHVycnIpCmBgYAoKSGVyZSdzIHRoZSBDKysgY29kZSB0byBsb2FkIHRoZSBgR2hvc3REZXRjdG9yQXJ0UmVjb3JkYCBhbmQgYFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkYCBkYXRhLiAKCmBgYHtSY3BwfQojaW5jbHVkZSA8UmNwcC5oPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8c3RyaW5nPgoKLy8gW1tSY3BwOjpkZXBlbmRzKGdhbGxlcnlHbTJSKV1dCgojaW5jbHVkZSAiY2FudmFzL1V0aWxpdGllcy9JbnB1dFRhZy5oIgojaW5jbHVkZSAiZ2FsbGVyeS9FdmVudC5oIgojaW5jbHVkZSAiZ2FsbGVyeS9WYWxpZEhhbmRsZS5oIgoKI2luY2x1ZGUgIkdob3N0RGV0ZWN0b3JBcnRSZWNvcmRERi5oaCIKI2luY2x1ZGUgIlRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkREYuaGgiCiNpbmNsdWRlICJHYWxsZXJ5VGltZXIuaGgiCgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKLy8gW1tSY3BwOjpleHBvcnRdXQpSY3BwOjpMaXN0IGdldERGcyh2ZWN0b3I8c3RyaW5nPiBmaWxlcykgewogIGlmICggZmlsZXMuc2l6ZSgpIDwgMSkgeyBSY3BwOjpzdG9wKCJZb3UgbXVzdCBzcGVjaWZ5IGF0IGxlYXN0IG9uZSBmaWxlIHRvIHByb2Nlc3MiKTsgfQogCiAgLy8gSW5wdXQgdGFncwogIGFydDo6SW5wdXRUYWcgY29uc3QgZ2hvc3RfdGFneyJhcnRnNDpHaG9zdFJpbmdEZXRlY3RvciJ9OwogIGFydDo6SW5wdXRUYWcgY29uc3QgdHJhY2tpbmdBY3Rpb25fdGFneyJhcnRnNDoifTsKICAKICAvLyBUaW1lcgogIEdhbGxlcnlUaW1lciB0aW1lcjsKIAogIC8vIERhdGFmcmFtZXMKICBHaG9zdERldGVjdG9yQXJ0UmVjb3JkREYgZ0RGOwogIFRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkREYgdGFERjsKIAogIC8vIExvb3Agb3ZlciBldmVudHMKICBmb3IgKCBnYWxsZXJ5OjpFdmVudCBldihmaWxlcyk7ICFldi5hdEVuZCgpOyBldi5uZXh0KCkgKSB7CiAgICB0aW1lci5iZWdpblRpbWluZygpOwogIAogICAgZ0RGLmZpbGwoZXYsIGdob3N0X3RhZyk7CiAgICB0YURGLmZpbGwoZXYsIHRyYWNraW5nQWN0aW9uX3RhZyk7CgogICAgdGltZXIuZW5kVGltaW5nKCk7IAogIH0KICAKICB0aW1lci53cml0ZSgpOwogIAogIHJldHVybiBSY3BwOjpMaXN0OjpjcmVhdGUoCiAgICBSY3BwOjpOYW1lZCgiZ0RGIikgID0gZ0RGLmRmKCksCiAgICBSY3BwOjpOYW1lZCgidGFERiIpID0gdGFERi5kZigpCiAgKTsKfQpgYGAKClJlbmVlIGhhcyBhIGZpbGUgdGhhdCBJIGNhbiBnZXQgdG8gaW4gWFJvb3RELiBJIHRoaW5rIHRoaXMgb25lIGhhcyBldmVyeXRoaW5nIHR1cm5lZCBvbi4KCmBgYHtyfQpzeXN0ZW0oCiAgImlmZGggbHMgL3BuZnMvR00yL3NjcmF0Y2gvdXNlcnMvcmZhdGVtaS9NSWd1bi9nbTJyaW5nc2ltX211b25fQS5yb290IiwgaW50ZXJuID0gVCkgJT4lIAogICAgICAgcGFzdGUwKCdyb290Oi8vZm5kY2ExLmZuYWwuZ292JywgLikgLT4gYUZpbGUKYGBgCgpMZXQncyByZWFkIGl0CgpgYGB7ciwgY2FjaGU9VFJVRX0KYWxsRGZzIDwtIGdldERGcyhhRmlsZSkKYGBgCgpgYGB7cn0KdGFkZiA8LSB0YmxfZGYoYWxsRGZzJHRhREYpCmdoZGYgPC0gdGJsX2RmKGFsbERmcyRnREYpCmBgYAoKCkhvdyBtdWNoIG1lbW9yeQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShwcnlyKQpgYGAKCmBgYHtyfQpvYmplY3Rfc2l6ZSh0YWRmKQpvYmplY3Rfc2l6ZShnaGRmKQpgYGAKCgojIEdldCBWb2x1bWVJRCBpbmZvcm1hdGlvbgoKV2UgbmVlZCB0byBjb252ZXJ0IHRoZSBWb2x1bWVJRCB0byBhIFZvbHVtZSBOYW1lIGZvciB0aGUgdHJhY2tpbmcgYWN0aW9uIGFydCByZWNvcmQuIFRoZSAicGh5c2ljYWwgdm9sdW1lIHN0b3JlIiB0YWJsZSBpcyBidXJpZWQgaW4gdGhlIGFydCBmaWxlIFJ1biByZWNvcmQsIHdoaWNoIGlzIGluYWNjZXNzaWJsZSB0byBnYWxsZXJ5LiBJIHdyb3RlIGEgc21hbGwgYW5hbHl6ZXIgdG8gc2ltcGx5IGR1bXAgdGhlIHBoeXNpY2FsIHZvbHVtZSBzdG9yZSAocHZzKSB0byBhIGZpbGUuIFJlbWVtYmVyIHRoYXQgdGhlIHB2cyBjYW4gY2hhbmdlIHBlciBmaWxlLgoKYGBge3IsIGNhY2hlPVRSVUV9CnJ1blRoaXMgPC0gc3RyX2ludGVycCgKICAiUFZTX0NTVk9VVD1tdW9uQS5jc3YgZ20yIC1jICR7ZmNsUGF0aH0vZmNsL3BoeXNpY2FsVm9sdW1lU3RvcmVUb0ZpbGUuZmNsIC1uIDEgJHthRmlsZX0iLAogIGxpc3QoZmNsUGF0aD1TeXMuZ2V0ZW52KCJHTTJBTkFMWVNFU19ESVIiKSwgYUZpbGU9YUZpbGUpKQoKc3lzdGVtKHJ1blRoaXMpCmBgYApMb2FkIHRoaXMgaW4uLi4KYGBge3J9Cm11b25BX3B2cyA8LSByZWFkX2NzdigibXVvbkEuY3N2IiwgY29sX25hbWVzID0gYygidm9sSUQiLCAidm9sTmFtZSIpKQpgYGAKV2hhdCBkaWQgd2UgZ2V0PwpgYGB7cn0KbXVvbkFfcHZzCmBgYAoKCldlIGNhbiB1c2UgdGhpcyBkYXRhIGZvciBtYWtpbmcgZmFjdG9ycy4KYGBge3J9CnRhZGYgJT4lIG11dGF0ZSggdm9sTmFtZSA9IGZhY3Rvcih2b2x1bWVVSUQsIGxldmVscz1tdW9uQV9wdnMkdm9sSUQsIGxhYmVscz1tdW9uQV9wdnMkdm9sTmFtZSApICkgLT4gdGFkZgpgYGAKCiMgTG9vayBhdCBwcmltYXJ5IG11b24gZGVhdGgKCkxldCdzIGp1c3QgbG9vayBhdCB0aGUgcHJpbWFyeSBtdW9ucyAobWF5YmUgbGF0ZXIgZG8gdGhpcyBzZWxlY3Rpb24gaW4gdGhlIGdhbGxlcnkgY29kZSkgYW5kIHdlIGRvbid0IHdhbnQgdGhlIGJpcnRoLiBXZSBvbmx5IG5lZWQgdGhlIHRyYWNraW5nIGFjdGlvbiBkYXRhIGZvciB0aGlzLiAKYGBge3J9CnRhZGYgJT4lIGZpbHRlcihwYXJlbnRUcmFja0lEID09IDAsIHN0YXR1cyAhPSAwKSAtPiBwbWRmCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKYGBgCgojIyBEZWF0aCBieSB2b2x1bWUgZnJvbSB0cmFja2luZyBhY3Rpb24KClBsb3QgdGhlIHZvbHVtZSBmb3IgdGhlIGR5aW5nIG11b24uIApgYGB7cn0KcG1kZiAlPiUgZ2dwbG90KGFlcyh2b2xOYW1lKSkgKyBnZW9tX2JhcigpCmBgYAoKYGBge3J9CnBtZGYgJT4lIGNvdW50KHZvbE5hbWUpICU+JSBtdXRhdGUocGVyY2VudD1uL3N1bShuKSoxMDApCmBgYAoKIyMgRGVhdGggYnkgcG9zaXRpb24KClRoZSBvbmVzIGluIHRoZSBhcmMgYXJlIHRoZSBkZWNheXMgdG8gcG9zaXRyb25zIChhbmQgdGhleSBuaWNlbHkgc2hvdyB3aGVyZSB0aGUgcmluZyBpcykuIFRoZSBvbmVzIHRoYXQgaGl0IHRoZSB3b3JsZCBhcmUgbG9zdC4gCgoKYGBge3J9CnBtZGYgJT4lIHBsb3RfbHkoeD1+bWFwX2RibChwb3MsIDEpLCB5PX5tYXBfZGJsKHBvcywgMyksIHo9fm1hcF9kYmwocG9zLDIpLAogICAgICAgICAgICAgICAgIGNvbG9yPX5hcy5jaGFyYWN0ZXIodm9sTmFtZSksCiAgICAgICAgICAgICAgICAgY29sb3JzID0gYygnI0JGMzgyQScsICcjMEM0QjhFJykgKSAlPiUgCiAgYWRkX21hcmtlcnMoc2l6ZT1JKDIpKSAlPiUgCiAgbGF5b3V0KHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAneCcpLAogICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAneicpLAogICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAneScpKSkgLT4gcApwCgpgYGAKWW91IGNhbiBzcGluIHRoZSBwbG90IGFyb3VuZCBhbmQgem9vbSBpbiB3aXRoIHlvdXIgbW91c2UhCgojIyBQb3NpdGlvbiBhbmQgbW9tZW50dW0KCkxldCdzIHNlZSBpZiB3ZSBjYW4gYWRkIGxpbmVzIHNob3dpbmcgdGhlIG1vbWVudHVtIGRpcmVjdGlvbi4uLgoKTGV0J3MgY29udmVydCB0aGUgbW9tZW50dW0gdmVjdG9ycyBpbnRvIHVuaXQgdmVjdG9ycyAobm90ZSB0aGF0IGBwYCBpcyBpbmRlZWQgdGhlIG1hZ25pdHVkZSBvZiB0aGUgYG1vbWAgdmVjdG9yKS4uLgpgYGB7cn0KcG1kZiAlPiUgbXV0YXRlKCBpcHggPSBtYXBfZGJsKG1vbSwxKS9wLCBpcHkgPSBtYXBfZGJsKG1vbSwyKS9wLCBpcHogPSBtYXBfZGJsKG1vbSwzKS9wICkgLT4gcG1kZgpgYGAKCkFwcGFyZW5sdHksIFBsb3RseSBjYW4ndCBkcmF3IDNEIHNlZ21lbnRzIChidW1tZXIhKS4gU3dpdGNoIHRvIGByZ2xgLgoKYGBge3J9CmxpYnJhcnkocmdsKQpgYGAKCk5lZWQgYSB2ZWN0b3IgZm9yIGNvbG9yaW5nIGJ5IHZvbHVtZSBuYW1lLi4uCmBgYHtyfQp2b2xDb2xvciA8LSB3aXRoKHBtZGYsCiAgICAgICAgICAgICAgICAgaWZlbHNlKHZvbE5hbWU9PSd3b3JsZCcsICdibGFjaycsICdyZWQnKQopCmBgYAoKCmBgYHtyfQpwbG90M2QoeD1tYXBfZGJsKHBtZGYkcG9zLCAxKSwgeT1tYXBfZGJsKHBtZGYkcG9zLCAzKSwgej1tYXBfZGJsKHBtZGYkcG9zLCAyKSwKICAgICAgIHR5cGU9InAiLAogICAgICAgeGxhYj0ieCAobW0pIiwgeWxhYj0ieiAobW0pIiwgemxhYj0ieSAobW0pIiwgY29sPXZvbENvbG9yLCAKICAgICAgIG1haW4gPSAnbXUrIGRlYXRoIHBvc2l0aW9uJywKICAgICAgIHN1YiA9ICdyZWQ9ZGVjYXkgaW4gcmluZywgYmxhY2s9ZXhpdCBhdCB3b3JsZCBib3VuZGFyeScKICAgICAgICkKcmdsd2lkZ2V0KCkKYGBgCgpMZXQncyBwbG90IHRoZSBtb21lbnR1bSB1bml0IHZlY3RvcnMgCgoKYGBge3J9CmludGVybGVhdmUgPC0gZnVuY3Rpb24odjEsIHYyKSBhcy52ZWN0b3IocmJpbmQodjEsIHYyKSkKCnBsb3QzZCh4PW1hcF9kYmwocG1kZiRwb3MsIDEpLCB5PW1hcF9kYmwocG1kZiRwb3MsIDMpLCB6PW1hcF9kYmwocG1kZiRwb3MsIDIpLAogICAgICAgdHlwZT0icCIsCiAgICAgICB4bGFiPSJ4IiwgeWxhYj0ieiIsIHpsYWI9InkiLCBjb2w9dm9sQ29sb3IsIAogICAgICAgbWFpbj0ibXUrIGRlYXRoIHBvc2l0aW9uIGFuZCBtb21lbnR1bSB1bml0IHZlY3RvciIsCiAgICAgICBzdWIgPSAncmVkPWRlY2F5IGluIHJpbmcsIGJsYWNrPWV4aXQgYXQgd29ybGQgYm91bmRhcnknKQogICAgICAgCgojIEZvciBzZWdtZW50cywgd2UgaGF2ZSB0aGUgZm9sbG93aW5nCnMgPC0gNTAwCm0gPC0gZGF0YS5mcmFtZSgKICB4cyA9IG1hcF9kYmwocG1kZiRwb3MsMSksCiAgeXMgPSBtYXBfZGJsKHBtZGYkcG9zLDIpLAogIHpzID0gbWFwX2RibChwbWRmJHBvcywzKSwKICB4ZSA9IG1hcF9kYmwocG1kZiRwb3MsMSkgKyBwbWRmJGlweCpzLAogIHllID0gbWFwX2RibChwbWRmJHBvcywyKSArIHBtZGYkaXB5KnMsCiAgemUgPSBtYXBfZGJsKHBtZGYkcG9zLDMpICsgcG1kZiRpcHoqcywKICBtYWluID0gJ211KyBkZWF0aCBwb3NpdGlvbicsCiAgc3ViID0gJ3JlZD1kZWNheSBpbiByaW5nLCBibGFjaz1leGl0IGF0IHdvcmxkIGJvdW5kYXJ5JwopCgojIEludGVybGVhdmUgYW5kIGRpc3BsYXkKIyBTZWUgaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yNjg1MzcxNy91c2luZy1yZ2wtdG8tcGxvdC0zZC1saW5lLXNlZ21lbnRzLWluLXIKd2l0aChtLAogIHNlZ21lbnRzM2QoeD1pbnRlcmxlYXZlKHhzLCB4ZSksCiAgICAgICAgICAgICB5PWludGVybGVhdmUoenMsIHplKSwKICAgICAgICAgICAgIHo9aW50ZXJsZWF2ZSh5cywgeWUpLAogICAgICAgICAgICAgY29sPXJlcCh2b2xDb2xvciwgZWFjaD0yKSwgYWxwaGE9MC41CiAgKQopCnJnbHdpZGdldCgpCmBgYApBZ2FpbiwgeW91IGNhbiBzcGluIGFyb3VuZCB0aGUgaW1hZ2UgYW5kIHpvb20gaW4gYW5kIG91dC4gSSBjYW4ndCBkcmF3IGFycm93cyAoYnVtbWVyKS4gVGhlIG1vbWVudHVtIGRpcmVjdGlvbiBpcyBpbmRpY2F0ZWQgYnkgdGhlIGRpcmVjdGlvbiBvZiB0aGUgbGl0dGxlIGxpbmUgc2VnbWVudCAoaXQgc3RhcnRzIGF0IHRoZSBwb2ludCB3ZXJlIHRoZSBtdW9uIGlzIGxvc3Q7IHRoZSBkb3QgLSBhbGwgb2YgdGhlIGJsYWNrIGRvdHMgYXJlIGF0IHRoZSB3b3JsZCB2b2x1bWUgZWRnZSkuIFRoZSBsZW5ndGggaXMgbWVyZWx5IHRoZSBzYW1lIGZvciBhbGwgc2VnbWVudHMgKG9ubHkgc2hvd3MgdGhlIG1vbWVudHVtIGRpcmVjdGlvbiwgbm90IGl0cyBtYWduaXR1ZGUpLgoKSW5zdGVhZCBvZiB1c2luZyBhIHVuaXQgdmVjdG9yIGZvciB0aGUgbW9tZW50dW0sIGxldCdzIHRyeSB0aGUgcmVhbCB2ZWN0b3IuIFRoZXknbGwgbGlrZWx5IG5lZWQgdG8gYmUgc2NhbGVkIGRvd24uIEl0IHdvdWxkIGFsc28gYmUgaGVscGZ1bCB0byBkcmF3IHdoZXJlIHRoZSBpbmZsZWN0b3IgYW5kIGtpY2tlciBtYW5nZXRzIGFyZS4gCgoKCgoKYGBge3J9CgpjbGVhcjNkKCkKcyA8LSAwLjUKCndpdGgocG1kZiwgewogICAgcGxvdDNkKHg9bWFwX2RibChwb3MsIDEpLCB5PW1hcF9kYmwocG9zLCAzKSwgej1tYXBfZGJsKHBvcywgMiksCiAgICAgICAgICB0eXBlPSJwIiwKICAgICAgICAgIHhsYWI9IngiLCB5bGFiPSJ6IiwgemxhYj0ieSIsIGNvbD12b2xDb2xvciwKICAgICAgICAgIG1haW49Im11KyBkZWF0aCBwb3NpdGlvbiBhbmQgbW9tZW50dW0gdmVjdG9yIiwKICAgICAgICAgIHN1YiA9ICdyZWQ9ZGVjYXkgaW4gcmluZywgYmxhY2s9ZXhpdCBhdCB3b3JsZCBib3VuZGFyeScKICAgICkKICAKICAgIHNlZ21lbnRzM2QoeCA9IGludGVybGVhdmUobWFwX2RibChwb3MsIDEpLCBtYXBfZGJsKHBvcywgMSkgKyBtYXBfZGJsKG1vbSwgMSkqcyksCiAgICAgICAgICAgICAgIHkgPSBpbnRlcmxlYXZlKG1hcF9kYmwocG9zLCAzKSwgbWFwX2RibChwb3MsIDMpICsgbWFwX2RibChtb20sIDMpKnMpLAogICAgICAgICAgICAgICB6ID0gaW50ZXJsZWF2ZShtYXBfZGJsKHBvcywgMiksIG1hcF9kYmwocG9zLCAyKSArIG1hcF9kYmwobW9tLCAyKSpzKSwKICAgICAgICAgICAgICBjb2w9cmVwKHZvbENvbG9yLCBlYWNoPTIpCiAgICApCiAgICAKICAgIGN5bCA8LSBjeWxpbmRlcjNkKCBjZW50ZXI9cmJpbmQoYygwLDAsLTUwKSwgYygwLDAsNTApKSwKICAgICAgICAgICAgcmFkaXVzPTcxMTIsCiAgICAgICAgICAgIGUxPWNiaW5kKDAsMCwxKSwKICAgICAgICAgICAgZTI9Y2JpbmQoMSwwLDApLCBzaWRlcz0yMCwgY2xvc2VkID0gRikKICAgIHBsb3QzZChjeWwsIGFkZCA9IFQsIGFscGhhPTAuMSkKICAgIAogIH0KKQpyZ2x3aWRnZXQoKQpgYGAKCiMjIENvbm5lY3QgaGl0cyBpbiB0aGUgR2hvc3QgcmluZyBkZXRlY3RvciB0byB0aGUgd29ybGQgZXhpdAoKV291bGQgYmUgbmljZSB0byBjb25uZWN0IHRoZSBnaG9zdCBoaXRzIHRvIHRoZSB3b3JsZCBoaXRzIHRvIHNlZSBpZiB0aGluZ3MgYXJlIGNyb3NzaW5nIHRoZSByaW5nLiBMZXQncyB0cnkgdGhhdC4gCgpgYGB7cn0KaGVhZChnaGRmKQpgYGAKSnVzdCBwaWNrIHRoZSBtdW9ucwoKYGBge3J9CmdoZGYgJT4lIGZpbHRlcih0cmFja0lEID09IDEpIC0+IGdobWRmCmdobWRmCmBgYAoKQ29ubmVjdCB0aGVzZSB0byB0aGUgdHJhY2tpbmcgYWN0aW9uIGRhdGEgKHdlIG5lZWQgdG8gY2hvb3NlIG9ubHkgdGhlIG9uZXMgdGhhdCBlc2NhcGUgdGhlIHdvcmxkLCBub3QgdGhlIG9uZXMgdGhhdCBkZWNheSkuIFRoZSB3b3JsZCAoZm9yIHRoaXMgZmlsZSkgaXMgYHZvbElEID09IDE0YC4KCmBgYHtyfQpwbWRmICU+JSBmaWx0ZXIodm9sdW1lVUlEID09IDE0KSAtPiBwbXdkZgoKcGFzdGUoIm5yb3cocG13ZGYpPSIsIG5yb3cocG13ZGYpLCAiIG5yb3coZ2htZGYpPSIsIG5yb3coZ2htZGYpKQpgYGAKClVoIC0gd2h5IGFyZSB0aGVyZSBzbyBtYW55IGdob3N0IGhpdHM/IExvb2tzIGxpa2UgYXQgYSBwYXJ0aWN1bGFyIGV2ZW50LgpgYGB7cn0KcG13ZGYgJT4lIGZpbHRlcihldmVudEVudHJ5ID09IDQpIC0+IHBtdzRkZgpwbXc0ZGYKYGBgCgoKYGBge3J9CmdobWRmICU+JSBmaWx0ZXIoZXZlbnRFbnRyeSA9PSA0KQpgYGAKCldoYWE/PyBUcnkgdG8gcGxvdCB0aGlzLiBXZSdsbCBwbG90IHRoZSBnaG9zdCBwb2ludHMgYW5kIGNvbm5lY3QgdGhlbSB3aXRoIGxpbmVzLiBBbmQgdGhlbiBwbG90IGEgbGluZSBmcm9tIHRoZSBsYXN0IGdob3N0IHBvaW50IHRvIHRoZSB3b3JsZCBleGl0LiAKCkdldCB0aGUgbGFzdCBnaG9zdCBwb2ludCBmb3IgdGhpcyBldmVudApgYGB7cn0KZ2htZGYgJT4lIGZpbHRlciggZXZlbnRFbnRyeSA9PSA0KSAlPiUgc2xpY2UobigpKSAtPiBnaG00ZGYKYGBgCgpNYWtlIGEgZGF0YSBmcmFtZSBmb3IgdGhpcyBsYXN0IHBvaW50CmBgYHtyfQpnaDJ3ZCA8LSBkYXRhLmZyYW1lKCB4PWMobWFwX2RibChnaG00ZGYkcG9zLCAxKSwgbWFwX2RibChwbXc0ZGYkcG9zLCAxKSksCiAgICAgICAgICAgICAgICAgICAgIHk9YyhtYXBfZGJsKGdobTRkZiRwb3MsIDIpLCBtYXBfZGJsKHBtdzRkZiRwb3MsIDIpKSwKICAgICAgICAgICAgICAgICAgICAgej1jKG1hcF9kYmwoZ2htNGRmJHBvcywgMyksIG1hcF9kYmwocG13NGRmJHBvcywgMykpKQpnaDJ3ZApgYGAKCgoKYGBge3J9CnJnbC5jbGVhcigpCgp3aXRoKCBnaG1kZiAlPiUgZmlsdGVyKGV2ZW50RW50cnkgPT0gNCksewogICAgcGxvdDNkKCB4ID0gbWFwX2RibChwb3MsIDEpLAogICAgICAgICAgICB5ID0gbWFwX2RibChwb3MsIDMpLAogICAgICAgICAgICB6ID0gbWFwX2RibChwb3MsIDIpLCB0eXBlPSJwIiwgY29sPSJncmVlbiIsCiAgICAgICAgICAgIHhsaW09YygtMTAwMDAsIDEwMDAwKSwgeWxpbT1jKC0xMDAwMCwgMTAwMDApLCB6bGltPWMoLTYwMCwgNjAwKSwKICAgICAgICAgICAgeGxhYj0neCcsIHlsYWI9J3onLCB6bGFiPSd5JwogICAgKQogIAogICAgbGluZXMzZCh4ID0gbWFwX2RibChwb3MsIDEpLAogICAgICAgICAgICB5ID0gbWFwX2RibChwb3MsIDMpLAogICAgICAgICAgICB6ID0gbWFwX2RibChwb3MsIDIpICkKICAKICAgIGN5bCA8LSBjeWxpbmRlcjNkKCBjZW50ZXI9cmJpbmQoYygwLDAsLTUwKSwgYygwLDAsNTApKSwKICAgICAgICAgICAgICAgIHJhZGl1cz03MTEyLAogICAgICAgICAgICAgICAgZTE9Y2JpbmQoMCwwLDEpLAogICAgICAgICAgICAgICAgZTI9Y2JpbmQoMSwwLDApLCBzaWRlcz0yMCwgY2xvc2VkID0gRikKICAgIHBsb3QzZChjeWwsIGFkZCA9IFQsIGFscGhhPTAuMSkKfSkKCndpdGgoIHBtd2RmICU+JSBmaWx0ZXIoZXZlbnRFbnRyeSA9PSA0KSwgewogIHBvaW50czNkKCB4ID0gbWFwX2RibChwb3MsIDEpLAogICAgICAgICAgICB5ID0gbWFwX2RibChwb3MsIDMpLAogICAgICAgICAgICB6ID0gbWFwX2RibChwb3MsIDIpLCBjb2w9InJlZCIpCn0pCgp3aXRoKCBnaDJ3ZCwgCiAgICAgIGxpbmVzM2QoeD14LCB5PXosIHo9eSwgY29sPSJyZWQiKSkKcmdsd2lkZ2V0KCkKYGBgCgoKKlRoaXMgcmVhbGx5IG1ha2VzIG5vIHNlbnNlKi4gSSdtIHB1enpsZWQgYXMgdG8gd2h5IHRoZXJlIGFyZSBzbyBtYW55IGdob3N0IGhpdHMgd2l0aCBoYXJkIHR1cm5zLiBXZSBzaG91bGQgbG9vayBhdCB0aGUgc3RlcHMgaW4gUGFyYVZpZXcuIE9SIC0gd2UgY2FuIGxvb2sgYXQgdGhlIEdlYW50IHN0ZXBzCgojIyBXaGF0IHJlYWxseSB3b3VsZCBiZSBiZXR0ZXIgaXMgYSBjeWxpbmRyaWNhbCB3b3JsZCB2b2x1bWUgCgpXb3JraW5nIG9uIHRoaXMKCkFhcm9uIGxpa2VzICR4XjIkLiAKCgoK